import constants from '@/constants';
import { VerifyService } from '@/core/verify.service';
import { CurrentUser, Public } from '@/decorators';
import {
  UserAllowBatchDto,
  UserInviteeQueryDto,
  UserLoginDto,
  UserPass0Dto,
  UserPassDto,
  UserQueryDto,
  UserRpaImportDto,
  UserUpdateDto,
} from '@/dtos';
import { UserService } from '@/services/user.service';
import { compareHash, getHash, getNanoid, halt, ok } from '@/utils';
import {
  Body,
  Controller,
  Delete,
  Get,
  Headers,
  HttpStatus,
  Patch,
  Post,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import dayjs from 'dayjs';
import { BaseController } from './base.controller';

@Controller('user')
export class UserController extends BaseController {
  constructor(
    private readonly jwtService: JwtService,
    private readonly verifyService: VerifyService,
    private readonly userService: UserService,
  ) {
    super();
  }

  @Post('invitee')
  async getInvitee(
    @Body() dto: UserInviteeQueryDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(
      await this.userService.getInvitee({
        ...dto,
        userId,
      }),
    );
  }

  @Public()
  @Post('login')
  async login(
    @Body()
    { type = 'captcha', mobile, captcha, code, pass }: UserLoginDto,
  ) {
    let user = await this.userService.findOne({
      mobile,
    });
    if (!!user && user.isBlockd) {
      return halt('账号已封禁或已注销');
    }

    if (type === 'captcha') {
      if (!!this.config.get('defaultCaptcha')) {
        if (
          !(await this.verifyService.verifyCode(mobile, captcha)) &&
          captcha !== this.config.get('defaultCaptcha')
        ) {
          return halt('验证码错误');
        }
      } else {
        if (!(await this.verifyService.verifyCode(mobile, captcha))) {
          return halt('验证码错误');
        }
      }

      if (!user) {
        let bindCode = null;
        if (!!code) {
          const inviter = await this.userService.findOne({ code });
          if (!!inviter) {
            bindCode = inviter.code;
          }
        }
        const chain = await this.chain.createAccount(mobile);
        if (!chain) return halt('创建链账户失败');
        user = await this.userService.create({
          mobile,
          nickname: await getNanoid(10),
          account: chain.address,
          publicKey: chain.publicKey,
          pass: pass ? await getHash(pass) : null,
          code: await getNanoid(8),
          bindCode,
        });
      }
    }

    if (type === 'password') {
      if (!user) {
        return halt('用户不存在');
      }
      if (!user.pass) {
        return halt('请先设置登录密码');
      }
      if (!(await compareHash(pass, user.pass))) {
        return halt('密码错误');
      }
    }

    await this.userService.update({
      where: { id: user.id },
      data: { lastAccessAt: new Date() },
    });

    const iss = user.id;
    const jti = await getNanoid();
    const nbf = dayjs().unix();
    const iat = nbf;
    const exp = dayjs().add(15, 'days');

    await this.redis.set(
      `token:${user.id}`,
      jti,
      'EX',
      exp.diff(dayjs(), 'seconds'),
    );

    return ok({
      type: 'Bearer',
      token: await this.jwtService.signAsync({
        id: user.id,
        mobile: user.mobile,
        realname: user.realname,
        // 编号
        jti,
        // 签发人
        iss,
        // 过期时间
        exp: exp.unix(),
        // 生效时间
        nbf,
        // 签发时间
        iat,
        // 主题
        sub: constants.jwt.subs.web,
        // 受众
        aud: constants.jwt.auds.web,
      }),
      expire: exp.format('YYYY-MM-DD HH:mm:ss'),
    });
  }

  @Get('owner')
  async getOwner(
    @CurrentUser('id') id: number,
    @Headers('authorization') authorization: string,
  ) {
    // #region 统计活跃用户
    const key = `visit:${dayjs().format('YYYY-MM-DD')}`;
    if (!(await this.redis.exists(key))) {
      await this.redis.sadd(key, id);
      await this.redis.expire(key, 24 * 60 * 60);
    } else {
      await this.redis.sadd(key, id);
    }
    // #endregion
    const user = await this.userService.findOne(
      {
        id,
      },
      {
        id: true,
        balance: true,
        frozen: true,
        mobile: true,
        realname: true,
        nickname: true,
        points: true,
        diamonds: true,
        experience: true,
        idCard: true,
        avatar: true,
        code: true,
        bindCode: true,
        account: true,
        isReal: true,
        isHideHold: true,
        isHideOrder: true,
        isBatch: true,
        camp: true,
      },
    );
    if (!user) {
      return halt('用户信息异常');
    }

    const token = authorization.toString().replace('Bearer ', '');
    const res = this.jwtService.verify(token);
    const jti = await this.redis.get(`token:${res.id}`);
    if (!!jti && jti !== res.jti) {
      return halt('登录已过期', HttpStatus.UNAUTHORIZED);
    }

    return ok({
      ...user,
      balance: Number(user.balance),
      frozen: Number(user.frozen),
      points: Number(user.points),
      diamonds: Number(user.diamonds),
    });
  }

  @Patch('owner')
  async updateOwner(@CurrentUser('id') id: number, @Body() dto: UserUpdateDto) {
    return ok(
      await this.userService.update({
        where: {
          id,
        },
        data: {
          nickname: dto.nickname,
          avatar: dto.avatar,
          isHideHold: dto.isHideHold,
          isHideOrder: dto.isHideOrder,
        },
      }),
    );
  }

  @Patch('pass')
  async setTxPass(
    @CurrentUser('mobile') mobile: string,
    @Body() { captcha, pass, passConfirm }: UserPassDto,
  ) {
    if (!!this.config.get('defaultCaptcha')) {
      if (
        !(await this.verifyService.verifyCode(mobile, captcha)) &&
        captcha !== this.config.get('defaultCaptcha')
      ) {
        return halt('验证码错误');
      }
    } else {
      if (!(await this.verifyService.verifyCode(mobile, captcha))) {
        return halt('验证码错误');
      }
    }
    if (pass !== passConfirm) {
      return halt('两次密码不一致');
    }
    await this.userService.update({
      where: {
        mobile,
      },
      data: {
        txPass: await getHash(pass),
      },
    });
    return ok(true);
  }

  @Patch('pass0')
  async setPass(
    @CurrentUser('mobile') mobile: string,
    @Body() { captcha, pass, passConfirm }: UserPass0Dto,
  ) {
    if (!!this.config.get('defaultCaptcha')) {
      if (
        !(await this.verifyService.verifyCode(mobile, captcha)) &&
        captcha !== this.config.get('defaultCaptcha')
      ) {
        return halt('验证码错误');
      }
    } else {
      if (!(await this.verifyService.verifyCode(mobile, captcha))) {
        return halt('验证码错误');
      }
    }
    if (pass !== passConfirm) {
      return halt('两次密码不一致');
    }
    await this.userService.update({
      where: {
        mobile,
      },
      data: {
        pass: await getHash(pass),
      },
    });
    return ok(true);
  }

  @Delete('close')
  async close(@CurrentUser('id') id: number) {
    await this.userService.update({
      where: {
        id,
      },
      data: {
        isBlockd: true,
      },
    });
    return ok(true);
  }

  @Post('admin/query')
  async query(@Body() dto: UserQueryDto) {
    return ok(await this.userService.query(dto));
  }

  @Patch('admin/allow-batch')
  async allowBatch(@Body() dto: UserAllowBatchDto) {
    return ok(await this.userService.allowBatch(dto));
  }

  @Patch('admin/update')
  async update(@Body() dto: any) {
    return ok(
      await this.prisma.user.update({
        where: {
          ...dto.where,
        },
        data: {
          ...dto.data,
        },
      }),
    );
  }

  @Post('rpa/import')
  async rpaImport(@Body() dto: UserRpaImportDto) {
    for (let row of dto.rows) {
      try {
        const user = await this.userService.findOne({
          mobile: row.mobile,
        });
        if (!user) {
          const chain = await this.chain.createAccount(row.mobile);
          if (!chain) return halt('创建链账户失败');
          await this.userService.create({
            mobile: row.mobile,
            nickname: await getNanoid(10),
            account: chain.address,
            publicKey: chain.publicKey,
            pass: '$2a$10$juz/bf0p3dpjJPVDIeeFRO07.eN84xh0jg.rjt8ZGvmZ.qWdS.RS6',
            txPass:
              '$2a$10$juz/bf0p3dpjJPVDIeeFRO07.eN84xh0jg.rjt8ZGvmZ.qWdS.RS6',
            code: await getNanoid(8),
            idCard: row.idCard,
            realname: row.realname,
            isReal: true,
            authAt: new Date(),
          });
        } else {
          await this.userService.update({
            where: {
              mobile: row.mobile,
            },
            data: {
              idCard: row.idCard,
              realname: row.realname,
              isReal: true,
              authAt: new Date(),
            },
          });
        }
      } catch (err) {
        console.log(err);
      }
    }
    return ok(true);
  }

  @Get('ranking/destroy')
  async destroyRanking(@CurrentUser('id') id: number) {
    return ok(await this.userService.destroyRanking(id));
  }
}
